#include <assert.h>
#include <getopt.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include "server.h"

/** Description of long options for getopt_long.  */
static const struct option long_options[] = {
    { "address",          1, NULL, 'a' },
    { "help",             0, NULL, 'h' },
    { "module-dir",       1, NULL, 'm' },
    { "port",             1, NULL, 'p' },
    { "verbose",          0, NULL, 'v' },
};

/** Description of short options for getopt_long.  */
static const char* const short_options = "a:hm:p:v";

/* Usage summary text.  */
static const char* const usage_template =
    "Usage: %s [ options ]\n"
    "  -a, --address ADDR        Bind to local address (by default, bind\n"
    "                              to all local addresses).\n"
    "  -h, --help                Print this information.\n"
    "  -m, --module-dir DIR      Load modules from specified directory\n"
    "                              (by default, use executable directory).\n"
    "  -p, --port PORT           Bind to specified port.\n"
    "  -v, --verbose             Print verbose messages.\n";

/** Print usage information and exit.  If IS_ERROR is nonzero, write to
    stderr and use an error exit code.  Otherwise, write to stdout and
    use a non-error termination code.  Does not return.  */
static void print_usage (int is_error) {
    fprintf (is_error ? stderr : stdout, usage_template, program_name);
    exit (is_error ? 1 : 0); 
}

int main (int argc, char* const argv[]) {
    struct in_addr local_address;
    uint16_t port;
    int next_option;

    /** Store the program name, which well use in error messages.  */
    program_name = argv[0];
    /** Set defaults for options.  Bind the server to all local addresses,
        and assign an unused port automatically.  */
    local_address.s_addr = INADDR_ANY;
    port = 0;
    /** Dont print verbose messages.  */
    verbose = 0;
    /** Load modules from the directory containing this executable.  */
    module_dir = get_self_executable_directory ();
    assert (module_dir != NULL);

    /** Parse options.  */
    do {
        next_option =
        getopt_long (argc, argv, short_options, long_options, NULL);
        switch (next_option) {
            case 'a':
                /** User specified -a or --address.  */
                {
                    struct hostent* local_host_name;
                    /** Look up the hostname the user specified.  */
                    local_host_name = gethostbyname (optarg);
                    if (local_host_name == NULL ||local_host_name->h_length == 0)
                        /** Could not resolve the name.  */
                        error (optarg, "invalid host name" );
                    else
                        /** Hostname is OK, so use it.  */
                        local_address.s_addr = *((int*) (local_host_name->h_addr_list[0]));
                }
                break;
            case 'h':
                /** User specified -h or --help.  */
                print_usage (0);
            case 'm':
                /** User specified -m or --module-dir.  */
                {
                    struct stat dir_info;
                    /** Check that it exists.  */
                    if (access (optarg, F_OK) != 0)
                        error (optarg, "module directory does not exist");
                    /** Check that it is accessible.  */
                    if (access (optarg, R_OK |X_OK) != 0)
                        error (optarg, "module directory is not accessible");
                    /** Make sure that it is a directory.  */
                    if (stat (optarg, &dir_info) != 0 ||!S_ISDIR (dir_info.st_mode))
                        error (optarg, "not a directory");
                    /** It looks OK, so use it.  */
                    module_dir = strdup (optarg);
                }
                break;
            case 'p':
                /** User specified -p or --port.  */
                {
                    long value;
                    char* end;
                    value = strtol (optarg, &end, 10);
                    if (*end != '\0')
                    /** The user specified nondigits in the port number.  */ 
                    print_usage (1);
                    /** The port number needs to be converted to network (big endian) 
                        byte order.  */
                    port = (uint16_t) htons (value);
                }
                break;
            case 'v':
                /** User specified -v or --verbose.  */ 
                verbose = 1;
                break; 
            case '?':
                /** User specified an unrecognized option.  */ 
                print_usage (1);
            case -1:
                /** Done with options.  */
                break;
            default:
                abort ();
        }
    } while (next_option != -1);
    /** This program takes no additional arguments.  Issue an error if the 
        user specified any.  */
    if (optind != argc)
        print_usage (1);
    /** Print the module directory, if were running verbose.  */
    if (verbose)
        printf ("modules will be loaded from %s\n", module_dir);
    /** Run the server.  */
        server_run (local_address, port);
    return 0;
}
